/*=============================================================================
PostProcess.cpp : Post process main interface
Copyright (c) 2001 Crytek Studios. All Rights Reserved.

Revision history:
* Created by Tiago Sousa

=============================================================================*/

#include "StdAfx.h"
#include "PostEffects.h"

void CParamFloat::SetParam(float fParam, bool bForceValue)
{
  if( m_bSmoothTransition && CRenderer::CV_r_PostProcessParamsBlending )
  {    
    if (!bForceValue)
    {
      if( !m_nFrameSetCount)
      {    
        m_fFrameParamAcc = m_fParamDefault;
      }

      m_fFrameParamAcc += fParam;    
      if( !m_nFrameSetCount && m_fParamDefault )
        m_nFrameSetCount += 2;    // Make sure to initialize frame set count to 2, for non 0 default values
      else
        m_nFrameSetCount++;
    }
    else
    {
      m_fParam = m_fFrameParamAcc = fParam;
      m_nFrameSetCount = 0;
    }
  }
  else  
    m_fParam = fParam;  
}

float CParamFloat::GetParam()
{
  return m_fParam;
}

void CParamFloat::SyncMainWithRender()
{
	if( m_bSmoothTransition && CRenderer::CV_r_PostProcessParamsBlending )
	{
		if( m_nFrameSetCount )              
		{
			m_fFrameParamAcc /= (float) m_nFrameSetCount;
			m_nFrameSetCount = 0;
		}

		float fFrameTime = clamp_tpl<float>(gEnv->pTimer->GetFrameTime()*CRenderer::CV_r_PostprocessParamsBlendingTimeScale, 0.0f, 1.0f);  
		m_fParam += (m_fFrameParamAcc - m_fParam) * fFrameTime;
	}
}

void CParamVec4::SetParamVec4( const Vec4 &pParam, bool bForceValue )
{
  if( m_bSmoothTransition && CRenderer::CV_r_PostProcessParamsBlending )
  {
    if (!bForceValue)
    {
      if( !m_nFrameSetCount)
        m_pFrameParamAcc = m_pParamDefault;

      m_pFrameParamAcc += pParam;

      if( !m_nFrameSetCount && (m_pParamDefault.x +  m_pParamDefault.y + m_pParamDefault.z + m_pParamDefault.w ))
        m_nFrameSetCount += 2;  // Make sure to initialize frame set count to 2, for non 0 default values
      else
        m_nFrameSetCount++;
    }
    else
    {
      m_pParam = m_pFrameParamAcc = pParam;
      m_nFrameSetCount = 0;
    }
  }
  else
    m_pParam = pParam;  
}
 
Vec4 CParamVec4::GetParamVec4()
{
  return m_pParam;
}
 
void CParamVec4::SyncMainWithRender()
{
  if( m_bSmoothTransition && CRenderer::CV_r_PostProcessParamsBlending )
  {  
    if( m_nFrameSetCount )              
    {
      m_pFrameParamAcc /= (float) m_nFrameSetCount;

      m_nFrameSetCount = 0;
    }

    float fFrameTime = clamp_tpl<float>(gEnv->pTimer->GetFrameTime()*CRenderer::CV_r_PostprocessParamsBlendingTimeScale, 0.0f, 1.0f);  
    m_pParam += (m_pFrameParamAcc - m_pParam) * fFrameTime;
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

int CPostEffectsMgr::Init()
{ 
  m_bPostReset = false;
  ClearCache();

  // Initialize the CRC table
  // This is the official polynomial used by CRC-32
  // in PKZip, WinZip and Ethernet.
  unsigned int ulPolynomial = 0x04c11db7;

  // 256 values representing ASCII character codes.
  for (int i=0;i<=0xFF;i++)
  {
    m_nCRC32Table[i] = CRC32Reflect(i, 8) << 24;
    for (int j = 0; j < 8; j++)
      m_nCRC32Table[i] = (m_nCRC32Table[i] << 1) ^ (m_nCRC32Table[i] & (1 << 31) ? ulPolynomial : 0);
    m_nCRC32Table[i] = CRC32Reflect(m_nCRC32Table[i], 32);
  } //i

  // Register default parameters
  AddParamFloat("Global_Brightness", m_pBrightness, 1.0f); // brightness
  AddParamFloat("Global_Contrast", m_pContrast, 1.0f);  // contrast
  AddParamFloat("Global_Saturation", m_pSaturation, 1.0f);  // saturation  

  AddParamFloat("Global_ColorC", m_pColorC, 0.0f);  // cyan amount
  AddParamFloat("Global_ColorY", m_pColorY, 0.0f);  // yellow amount
  AddParamFloat("Global_ColorM", m_pColorM, 0.0f);  // magenta amount
  AddParamFloat("Global_ColorK", m_pColorK, 0.0f);  // darkness amount

  AddParamFloat("Global_ColorHue", m_pColorHue, 0.0f);  // image hue rotation  

  // User parameters
  AddParamFloat("Global_User_Brightness", m_pUserBrightness, 1.0f); // brightness
  AddParamFloat("Global_User_Contrast", m_pUserContrast, 1.0f);  // contrast
  AddParamFloat("Global_User_Saturation", m_pUserSaturation, 1.0f);  // saturation  

  AddParamFloat("Global_User_ColorC", m_pUserColorC, 0.0f);  // cyan amount
  AddParamFloat("Global_User_ColorY", m_pUserColorY, 0.0f);  // yellow amount
  AddParamFloat("Global_User_ColorM", m_pUserColorM, 0.0f);  // magenta amount
  AddParamFloat("Global_User_ColorK", m_pUserColorK, 0.0f);  // darkness amount

  AddParamFloat("Global_User_ColorHue", m_pUserColorHue, 0.0f);  // image hue rotation  

  AddParamVec4("Global_DirectionalBlur_Vec", m_pDirectionalBlurVec, Vec4(0, 0, 0, 0)); // directional blur

	AddParamFloatNoTransition("Global_User_HDRBrightLevel", m_pUserHDRBrightLevel, 0.0f);  // image hue rotation  
	AddParamFloatNoTransition("Global_User_HDRBrightThreshold", m_pUserHDRBrightThreshold, 0.0f);  // image hue rotation  

	// Register all post processes
	AddEffect( CGammaReference );
	AddEffect( CSceneSnow );
	AddEffect( CSceneRain );
	AddEffect( CSunShafts );
	AddEffect( CGlow );
	AddEffect( CDepthOfField );
	AddEffect( CMotionBlur );
	AddEffect( CUnderwaterGodRays );
	AddEffect( CVolumetricScattering );
	AddEffect( CRainDrops );
	AddEffect( CWaterDroplets );
	AddEffect( CWaterFlow );
	AddEffect( CScreenFrost );
	AddEffect( CAlienInterference );
	AddEffect( CFlashBang );
	AddEffect( CFilterSharpening );
	AddEffect( CFilterBlurring );
	AddEffect( CColorGrading );
	AddEffect( CNightVision );
	AddEffect( CHudSilhouettes );
	AddEffect( CSonarVision );
	AddEffect( CThermalVision );
	AddEffect( CImageGhosting );
	AddEffect( CWaterRipples );
	AddEffect( CWaterVolume );
	AddEffect( CPostAA );
	AddEffect( CPostStereo );
	AddEffect( C3DHud );
	AddEffect( CFilterKillCamera );
	AddEffect( CNanoGlass );
	AddEffect( CHUDHitEffect );
	AddEffect( CUberGamePostProcess );
	AddEffect( CSoftAlphaTest );
	AddEffect( CScreenBlood );
	AddEffect( CScreenGlassShards );
	AddEffect( CScreenFog );
	AddEffect( CPost3DRenderer );
		
  // Sort all post effects by ID
  std::sort(m_pEffects.begin(), m_pEffects.end(), SortEffectsByID);

  // Initialize all post process techniques    
  std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectInitialize());
  m_bCreated = false;

  // Initialize parameters
  StringEffectMapItor pItor= m_pNameIdMapGen.begin(), pEnd = m_pNameIdMapGen.end();
  for( ; pItor!=pEnd ; ++pItor)
  {            
		for(int t= 0; t <RT_COMMAND_BUF_COUNT; ++t)
		{
			m_pNameIdMap[t].insert( KeyEffectMapItor::value_type( GetCRC(pItor->first.c_str()), pItor->second ) );
		}
  }
  
  m_pNameIdMapGen.clear(); 

  return 1;
}

void CPostEffectsMgr::CreateResources()
{ 
  // Initialize all post process techniques    
  if (!m_bCreated)
    std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectCreateResources());
  m_bCreated = true;
}
void CPostEffectsMgr::ReleaseResources()
{ 
  if (m_bCreated)
  {
    std::for_each(m_pEffects.begin(), m_pEffects.end(), container_object_safe_release());
    for (size_t buff = 0; buff < RT_COMMAND_BUF_COUNT; ++ buff)
    {
      for (size_t recursion = 0; recursion < MAX_REND_RECURSION_LEVELS; ++ recursion)
      {
        m_pPostProcLights[buff][recursion].Free();
      }
    }
  }
  m_bCreated = false;
}

/*void CPostEffectsMgr::TidyResources()
{
  std::for_each(m_pEffects.begin(), m_pEffects.end(), container_object_safe_tidier());
	for (size_t buff = 0; buff < RT_COMMAND_BUF_COUNT; ++ buff)
	{
		for (size_t recursion = 0; recursion < MAX_REND_RECURSION_LEVELS; ++ recursion)
		{
			m_pPostProcLights[buff][recursion].Free();
		}
	}
}*/

void CPostEffectsMgr::Release()
{ 
  // Free all resources
  ClearCache();
	
	std::for_each(m_pNameIdMap[0].begin(), m_pNameIdMap[0].end(), SContainerKeyEffectParamDelete());  
	for(int t = 0; t < RT_COMMAND_BUF_COUNT; ++t)
		m_pNameIdMap[t].clear(); 
  
  std::for_each(m_pEffects.begin(), m_pEffects.end(), container_object_safe_release());
	std::for_each(m_pEffects.begin(), m_pEffects.end(), container_object_safe_delete());
  m_pEffects.clear();

  m_bCreated = false;
}

void CPostEffectsMgr::Reset(bool bOnSpecChange)
{
  ClearCache();

  m_pBrightness->ResetParam(1.0f);
  m_pContrast->ResetParam(1.0f);
  m_pSaturation->ResetParam(1.0f);
  m_pColorC->ResetParam(0.0f);
  m_pColorY->ResetParam(0.0f);
  m_pColorM->ResetParam(0.0f);
  m_pColorK->ResetParam(0.0f);
  m_pColorHue->ResetParam(0.0f);
  m_pUserBrightness->ResetParam(1.0f);
  m_pUserContrast->ResetParam(1.0f);
  m_pUserSaturation->ResetParam(1.0f);
  m_pUserColorC->ResetParam(0.0f);
  m_pUserColorY->ResetParam(0.0f);
  m_pUserColorM->ResetParam(0.0f);
  m_pUserColorK->ResetParam(0.0f);
  m_pUserColorHue->ResetParam(0.0f);
  m_pDirectionalBlurVec->ResetParamVec4(Vec4(0, 0, 0, 0));

	m_pUserHDRBrightLevel->ResetParam(0.0f);
	m_pUserHDRBrightThreshold->ResetParam(0.0f);

	if (bOnSpecChange)
		std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectResetOnSpecChange());
	else
		std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectReset());
}

int32 CPostEffectsMgr::GetEffectID(const char* pEffectName)
{
	int32 effectID = ePFX_Invalid;

	CPostEffectsMgr *pPostMgr = PostEffectMgr();
	for(CPostEffectItor pItor = pPostMgr->GetEffects().begin(), pItorEnd = pPostMgr->GetEffects().end(); pItor != pItorEnd; ++pItor)
	{
		CPostEffect *pCurrEffect = (*pItor);
		if(strcmp(pEffectName,pCurrEffect->GetName())==0)
		{
			effectID = pCurrEffect->GetID();
			break;
		}
	}

	return effectID;
}

void CPostEffectsMgr::OnLostDevice()
{
  std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectOnLostDevice());
}

void CPostEffectsMgr::OnBeginFrame()
{
	std::for_each(m_pEffects.begin(), m_pEffects.end(), SContainerPostEffectOnBeginFrame());
}

void CPostEffectsMgr::SyncMainWithRender()
{
	int nThreadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;

	KeyEffectMapItor iter;
	for(iter = m_pNameIdMap[nThreadID].begin(); iter != m_pNameIdMap[nThreadID].end(); iter++)
	{
		iter->second->SyncMainWithRender();
	}
}

// Used only when creating the crc table
uint32 CPostEffectsMgr::CRC32Reflect(uint32 ref, char ch)
{		
  uint32 value = 0;

  // Swap bit 0 for bit 7
  // bit 1 for bit 6, etc.
  for (int i = 1; i < (ch + 1); i++)
  {
    if(ref & 1)
      value |= 1 << (ch - i);
    ref >>= 1;
  }
  return value;
}

uint32 CPostEffectsMgr::GetCRC( const char *pszName )
{  
  if( !pszName )
  {
    assert("CPostEffectsMgr::GetCRC() invalid string passed");
    return 0;
  }

  // Once the lookup table has been filled in by the constructor,
  // this function creates all CRCs using only the lookup table.
  // Be sure to use unsigned variables, because negative values introduce high bits where zero bits are required.	
    
  const char *szPtr=pszName;	
  uint32 ulCRC=0xffffffff; // Start out with all bits set high.
  unsigned char c;

  while (*szPtr)
  {
    c=*szPtr++;
    if (c>='a' && c<='z')
      c-=32; //convert to uppercase
    ulCRC = (ulCRC >> 8) ^ m_nCRC32Table[(ulCRC & 0xFF) ^ c]; 
  }

// Exclusive OR the result with the beginning value...avoids the % operator 
  return ulCRC ^ 0xffffffff; 


}

CEffectParam *CPostEffectsMgr::GetByName(const char *pszParam)
{
  assert(pszParam || "mfGetByName: null FX name");

  uint32 nCurrKey = GetCRC( pszParam );

	int nThreadID = gRenDev->m_pRT ? gRenDev->m_pRT->GetThreadList() : 0;

  // cache per-thread
  ParamCache *pCache = &m_pParamCache[nThreadID];

  // Check cache first
  if( nCurrKey == pCache->m_nKey && pCache->m_pParam)
  {
    if( CRenderer::CV_r_PostProcess == 3)
      m_pEffectParamsUpdated[nThreadID].insert( StringEffectMapItor::value_type( pszParam, pCache->m_pParam ) );

    return pCache->m_pParam;
  }

  KeyEffectMapItor pItor = m_pNameIdMap[nThreadID].find( nCurrKey );

  if( pItor != m_pNameIdMap[nThreadID].end() )
  {
    pCache->m_pParam =  pItor->second;
    pCache->m_nKey = nCurrKey;

    if( CRenderer::CV_r_PostProcess == 3)
      m_pEffectParamsUpdated[nThreadID].insert( StringEffectMapItor::value_type( pszParam, pCache->m_pParam ) );

    return pCache->m_pParam;
  }

  return 0;
}

float CPostEffectsMgr::GetByNameF(const char *pszParam)
{    
  CEffectParam *pParam = GetByName(pszParam);
  if( pParam )
  {
    return pParam->GetParam();
  }

  return 0.0f;
}

Vec4 CPostEffectsMgr::GetByNameVec4(const char *pszParam)
{    
  CEffectParam *pParam = GetByName(pszParam);
  if( pParam )
  {
    return pParam->GetParamVec4();
  }

  return Vec4(0,0,0,0);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

int CPostEffectsMgr::SortEffectsByID( const CPostEffect *p1, const CPostEffect *p2)
{
  return (p1->GetID() < p2->GetID());
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

int CParamTexture::Create(const char *pszFileName)
{
  if(!pszFileName)
  {
    return 0;
  }

  if(m_pTexParam)
  {
    // check if texture is same
    if(!strcmpi(m_pTexParam->GetName(), pszFileName))
    {
      return 0;
    }
    // release texture if required
    SAFE_RELEASE(m_pTexParam)
  }

  m_pTexParam = CTexture::ForName(pszFileName, 0, eTF_Unknown);
  assert(m_pTexParam || "CParamTexture.Create: texture not found!");

  return 1;
}
const char *CParamTexture::GetParamString() const
{
  return m_pTexParam->GetName();
}

void CParamTexture::Release()
{
  SAFE_RELEASE(m_pTexParam);
}
